﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Downloader;
using HtmlAgilityPack;

namespace FieldTools.GetGiteeSoftPublish
{
    /// <summary>
    /// 下载gitee开源软件的发行版
    /// </summary>
    public class GetGiteeSoftPublishTool
    {
        /// <summary>
        /// 
        /// </summary>
        public GetGiteeSoftPublishTool()
        {
            //解决中文文件名解压乱码的情况
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        }
        /// <summary>
        /// 延迟自动退出
        /// </summary>
        /// <param name="count">延迟时间，单位S</param>
        /// <returns></returns>
        public async Task DelayExit(int count=5)
        {
            await Task.Run(async () =>
            {
                for (int i = count; i > 0; i--)
                {
                    Console.WriteLine($"{i}S后程序自动退出" + new string(' ', 10));
                    //清行处理操作
                    int currentLineCursor = Console.CursorTop;//记录当前光标位置
                    Console.SetCursorPosition(0, Console.CursorTop);//将光标至于当前行的开始位置
                                                                    //Console.WriteLine(new string(' ', Console.WindowWidth));
                    Console.SetCursorPosition(0, currentLineCursor - 1);//将光标恢复至开始时的位置
                    await Task.Delay(1000);
                    if (i == 1)
                    {
                        Console.WriteLine(new string(' ', Console.WindowWidth));
                    }
                }
            });
            System.Environment.Exit(0);   //这是最彻底的退出方式，不管什么线程都被强制退出，把程序结束的很干净，退出时有可能会抛出异常  
        }
        /// <summary>
        /// 下载的文件名集合
        /// </summary>
        public List<string> FileNames = new();
        /// <summary>
        /// 获取发行版
        /// </summary>
        /// <param name="config"></param>
        public async Task<bool> GetPublish(Config config)
        {
            if (config == null) return false;
            Console.WriteLine("~~~~~~~~~~~~~~~开始获取发行版文件~~~~~~~~~~~~~~~");
            var baseUrl = config.GiteeBasePath;
            var xpath = config.GiteeSoftPublishXPath;
            string faUr = null;
            var node = GetHtmlSignalNode(config.GiteeSoftPath, xpath);
            if (node != null)
            {
                faUr = baseUrl + node.Attributes["href"].Value;
                Console.WriteLine("获取到发行版链接: " + baseUrl + node.Attributes["href"].Value);
            }
            else
            {
                Console.WriteLine("未查询到发行版");
                return false;
            }

            if (faUr != null)
            {
                //获取更新日志
                var xpath2 = config.GiteePublishLogXPath;
                var node2 = GetHtmlSignalNode(faUr, xpath2);
                if (node2 != null)
                {
                    Console.WriteLine("获取到更新日志：\r\n" + node2.InnerText);
                }
                else
                {
                    Console.WriteLine("未获取到更新日志");
                }

                //获取下载链接
                var xpath3 = config.GiteePublishDownloadXPath;
                var nodex = GetHtmlNodes(faUr, xpath3);
                if (nodex != null)
                {
                    for (int i = 0; i < nodex.Count; i++)
                    {
                        var nodez = nodex[i];
                        try
                        {
                            var te = nodex[i].SelectSingleNode("a").Attributes["href"].Value;
                            //下载文件
                            if (te.Contains("download"))
                            {
                                var dowmLink = baseUrl + nodex[i].SelectSingleNode("a").Attributes["href"].Value;
                                Console.WriteLine("获取到打包链接：" + dowmLink);
                                var sa = System.IO.Directory.GetCurrentDirectory() + "/download/" + dowmLink.Split("/").Last();
                                var downloadOpt = new DownloadConfiguration()
                                {
                                    //BufferBlockSize = 10240, // 通常，主机最大支持8000字节，默认值为8000。
                                    //ChunkCount = 8, // 要下载的文件分片数量，默认值为1
                                    //MaximumBytesPerSecond = 1024 * 1024, // 下载速度限制为1MB/s，默认值为零或无限制
                                    //MaxTryAgainOnFailover = int.MaxValue, // 失败的最大次数
                                    //OnTheFlyDownload = false, // 是否在内存中进行缓存？ 默认值是true
                                    //ParallelDownload = true, // 下载文件是否为并行的。默认值为false
                                    //TempDirectory = "C:\\temp", // 设置用于缓冲大块文件的临时路径，默认路径为Path.GetTempPath()。
                                    //Timeout = 1000, // 每个 stream reader  的超时（毫秒），默认值是1000
                                    //RequestConfiguration = // 定制请求头文件
                                    //{
                                    //    Accept = "*/*",
                                    //    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
                                    //    CookieContainer =  new CookieContainer(), // Add your cookies
                                    //    Headers = new WebHeaderCollection(), // Add your custom headers
                                    //    KeepAlive = false,
                                    //    ProtocolVersion = HttpVersion.Version11, // Default value is HTTP 1.1
                                    //    UseDefaultCredentials = false,
                                    //    UserAgent = $"DownloaderSample/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}"
                                    //}
                                };
                                var downloader = new DownloadService(downloadOpt);
                                downloader.DownloadStarted += OnDownloadStarted;
                                downloader.ChunkDownloadProgressChanged += OnChunkDownloadProgressChanged;
                                downloader.DownloadProgressChanged += OnDownloadProgressChanged;
                                downloader.DownloadFileCompleted += OnDownloadFileCompleted;

                                await downloader.DownloadFileTaskAsync(dowmLink, sa);
                            }
                            else
                            {
                                Console.WriteLine("获取到源码链接（无法直接下载）：" + baseUrl + nodex[i].SelectSingleNode("a").Attributes["href"].Value);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            return false;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("未获取到下载连接");
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 覆盖更新
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public async Task HandleCoverUpdate(Config config)
        {
            if (!FileNames.Any()) return;
            Console.WriteLine("~~~~~~~~~~~~~~~开始覆盖更新操作~~~~~~~~~~~~~~~");
            string fullPathFileName = "";
            //解压文件
            if(config.Order > FileNames.Count)
            {
                fullPathFileName = FileNames[0];
            }
            else
            {
                fullPathFileName = FileNames[config.Order.Value];
            }

            string dirName = Path.GetFileNameWithoutExtension(fullPathFileName);
            string tempPath = Directory.GetCurrentDirectory() + "/temp/";
            string path = tempPath + dirName;
            if (Directory.Exists(path))//解压目录存在时删除
            {
                Directory.Delete(path, true);
            }
            ZipTool.ExtractToDirectory(fullPathFileName, path);
            Console.WriteLine($"解压文件到:{path}成功");
            //替换白名单文件（配置文件）
            if (config.WhiteList !=null && config.WhiteList.Any())
            {
                config.WhiteList.ForEach(white => {
                    if (File.Exists(white))
                    {
                        FileInfo fi1 = new FileInfo(white);
                        try
                        {
                            var te = $"{path}/{config.ExportDefaultName}/{white.Split('/').Last()}";
                            fi1.CopyTo(te, true);
                            Console.WriteLine($"文件：{white}文件复制到：{te}成功");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("文件复制失败："+ex.ToString());
                        }
                    }
                });
            }
            //重命名二级目录
            if(config.SecondLevelDirectory != null && config.SecondLevelDirectory != "")
            {
                if (Directory.Exists(path+"/"+config.ExportDefaultName))
                {
                    try
                    {
                        Directory.Move(path + "/" + config.ExportDefaultName, tempPath + config.SecondLevelDirectory);
                        Console.WriteLine($"二级目录重命名成功:dist-->{config.SecondLevelDirectory}");
                        if (Directory.Exists(path))
                        {
                            Directory.Delete(path, true);
                        }
                        //path = tempPath + config.SecondLevelDirectory;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("二级目录重命名失败："+ex.Message);
                    }
                    
                }
            }
            if (config.CoverPath != null)
            {
                //删除目标目录
                if (Directory.Exists(config.CoverPath))
                {
                    try
                    {
                        Directory.Delete(config.CoverPath, true);
                        Console.WriteLine($"目录：{config.CoverPath}已删除");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("目录删除失败：" + ex.Message);
                    }
                    
                }
                //复制目录
                var te = config.CoverPath.Remove(config.CoverPath.LastIndexOf('/'));
                CopyDirectory(tempPath, te);
                Console.WriteLine($"目录：{config.CoverPath}已更新");
                if (Directory.Exists(tempPath))//解压目录存在时删除
                {
                    Directory.Delete(tempPath, true);
                    Console.WriteLine($"临时目录{tempPath}已删除：");
                }
            }
        }
        /// <summary>
        /// 复制文件夹
        /// </summary>
        /// <param name="sourceDirPath"></param>
        /// <param name="SaveDirPath"></param>
        public static void CopyDirectory(string sourceDirPath, string SaveDirPath)
        {
            try
            {
                //如果指定的存储路径不存在，则创建该存储路径
                if (!Directory.Exists(SaveDirPath))
                {
                    //创建
                    Directory.CreateDirectory(SaveDirPath);
                }
                //获取源路径文件的名称
                string[] files = Directory.GetFiles(sourceDirPath);
                //遍历子文件夹的所有文件
                foreach (string file in files)
                {
                    string pFilePath = SaveDirPath + "/" + Path.GetFileName(file);
                    if (File.Exists(pFilePath))
                        continue;
                    File.Copy(file, pFilePath, true);
                }
                string[] dirs = Directory.GetDirectories(sourceDirPath);
                //递归，遍历文件夹
                foreach (string dir in dirs)
                {
                    CopyDirectory(dir, SaveDirPath + "/" + Path.GetFileName(dir));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// 下载完成的事件，可以包括发生错误或被取消或下载成功。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
        {
            fl = true;
            if (e.Cancelled)
            {
                Console.WriteLine("下载取消：");
                if (FileNames.Any())
                    FileNames.RemoveAt(FileNames.Count - 1);
                return;
            }
            if(e.Error != null)
            {
                Console.WriteLine("下载失败："+e.Error.Message);
                if(FileNames.Any())
                    FileNames.RemoveAt(FileNames.Count-1);
                return;
            }
            Console.WriteLine("下载结束");
        }
        /// <summary>
        /// 提供有关分块下载的信息，如每个分块的进度百分比、速度、收到的总字节数和收到的字节数组，以实现实时流。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnDownloadProgressChanged(object? sender, Downloader.DownloadProgressChangedEventArgs e)
        {
            //Console.WriteLine();
        }
        /// <summary>
        /// 计时器秒
        /// </summary>
        int sec = 1;
        /// <summary>
        /// 计时器秒old，用于减少刷新频率
        /// </summary>
        int oldSec = 1;
        /// <summary>
        /// 线程开关
        /// </summary>
        bool fl = false;
        /// <summary>
        /// 提供任何关于下载进度的信息，如进度百分比的块数总和、总速度、平均速度、总接收字节数和接收字节数组的实时流。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnChunkDownloadProgressChanged(object? sender, Downloader.DownloadProgressChangedEventArgs e)
        {
            if (sec > oldSec) return;
            Console.WriteLine("下载大小：" + e.ReceivedBytesSize + ",下载速度：" + e.ReceivedBytesSize / sec + "bytes/s，进度：" + e.ProgressPercentage.ToString(".00") +"%，花费时间："+sec+"S" + new string(' ', 10));
            //清行处理操作
            int currentLineCursor = Console.CursorTop;//记录当前光标位置
            Console.SetCursorPosition(0, Console.CursorTop);//将光标至于当前行的开始位置
            //Console.WriteLine(new string(' ', Console.WindowWidth));
            Console.SetCursorPosition(0, currentLineCursor - 1);//将光标恢复至开始时的位置
        }
        /// <summary>
        /// 在每次下载开始时提供 "文件名 "和 "要接收的总字节数"。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnDownloadStarted(object? sender, DownloadStartedEventArgs e)
        {
            Console.WriteLine("下载开始：" + e.FileName);
            Console.WriteLine("总字节数：" + e.TotalBytesToReceive);
            FileNames.Add(e.FileName);
            var ts = new Task(async () => {
                while (!fl)
                {
                    await Task.Delay(500);
                    sec++;
                    await Task.Delay(500);
                    oldSec++;
                }
            });
            ts.Start();
        }
        /// <summary>
        /// 获取xml单节点数据
        /// </summary>
        /// <param name="url"></param>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public HtmlNode? GetHtmlSignalNode(string url, string xpath)
        {
            var html = url;
            HtmlWeb web = new HtmlWeb();
            var htmlDoc = web.Load(html);
            try
            {
                var node = htmlDoc.DocumentNode.SelectSingleNode(xpath);
                return node;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }
        /// <summary>
        /// 获取xml多节点数据
        /// </summary>
        /// <param name="url"></param>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public HtmlNodeCollection GetHtmlNodes(string url, string xpath)
        {
            var html = url;
            HtmlWeb web = new HtmlWeb();
            var htmlDoc = web.Load(html);
            try
            {
                var nodes = htmlDoc.DocumentNode.SelectNodes(xpath);
                return nodes;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }
    }
}
